/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package plan;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import turnover.Consumption;
import save.*;

/**
 *
 * @author Alberto
 */
public class PlanFactory{
    private Vector orangePlans = new Vector();
    private Vector orangeSaves = new Vector();

    private Map combinations;
    private Consumption consumption;

    public PlanFactory(String company, Consumption consumption) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
        this.initialize();
        this.consumption = consumption;
        combinations = this.generateCombinations(company);
    }

    public void initialize(){
        orangeSaves.add("save.Orange_Bono_ahorroSMS");
        orangePlans.add("plan.mobile.Orange_tp_mini");
        orangePlans.add("plan.mobile.Orange_tp_24h");
        orangePlans.add("plan.mobile.Orange_Contrato_Decreciente");
        orangePlans.add("plan.mobile.Orange_Contrato_Libre");
        orangePlans.add("plan.mobile.Orange_Nuevo_Contrato_Unico");
        orangePlans.add("plan.mobile.Orange_tp");
        orangePlans.add("plan.mobile.Orange_tp_plus");
        orangePlans.add("plan.mobile.Vodafone_90x1_24h_Contrato");
        orangePlans.add("plan.mobile.Vodafone_Super_90x1_Contrato");
        


    }

    public Map getCombinations() {
        return combinations;
    }

    /**
     * Método para generar las combinaciones con los elementos que recibe.
     * @param elements Recibe los elementos.
     * @return Devuelve el ArrayList con las combinaciones.
     */
    private Map generateCombinations(String company) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
        Map res = new HashMap();
        Vector elements = null;
        Iterator itTarifa = null;
                itTarifa = this.orangePlans.iterator();
                elements = this.orangeSaves;
        int comb = (int) Math.pow(2, elements.size());
        for (int i = 1; i < comb; i++) {
            String binary = fillWithZeros(Integer.toBinaryString(i), elements.size());
            Vector v = this.getCombination(binary, elements);
            boolean combinationCompatible = this.combinationCompatible(v);
            String combination = "";
            double total = 0;
            while (itTarifa.hasNext() && combinationCompatible) {
                String namePlan = (String) itTarifa.next();
                Plan plan = (Plan) Class.forName(namePlan).newInstance();
                combination = "Plan: " + plan.getName();
                total = plan.getCost(consumption);
                res.put(combination, total);
                Iterator it = v.iterator();
                int flag = 0;
                boolean compatible = plan.isCompatible(v);
                while (it.hasNext() && compatible) {
                    String nameClass = (String) it.next();
                    combination += (" + " + nameClass);
                    Class t = Class.forName(nameClass);
                    Constructor cons = t.getDeclaredConstructor(Plan.class);
                    if(flag == 0){
                        plan = (Plan) cons.newInstance(plan);
                        flag++;
                    }else{
                        plan = (Plan) cons.newInstance((Save)plan);
                    }
                    
                }
                if(compatible){
                    total = ((Save)plan).getCost(consumption);
                    res.put(combination, total);
                }
            }
        }
        return res;
    }

    /**
     * Método para generar una combinación dada.
     * @param binary Recibe el String con un número en binario que dará el patrón de generación.
     * @param elements Recibe los elementos.
     * @return Devuelve la combinación.
     */
    private Vector<Object> getCombination(String binary, Vector<Object> elements) {
        Vector<Object> res = new Vector<Object>();
        for (int i = 0; i < binary.length(); i++) {
            if (Integer.parseInt(Character.toString(binary.charAt(i))) == 1) {
                res.add(elements.get(i));
            }
        }
        return res;
    }

    /**
     * Método para rellenar con ceros un número binario.
     *
     * Si recibe por ejemplo "010" y queremos que sea expresado con cinco cifras, devolverá: "00010".
     * @param binaryString Recibe el número en binario.
     * @param length Recibe el número de cifras que queremos que tenga el resultado.
     * @return Devuelve la cadena.
     */
    private String fillWithZeros(String binaryString, int length) {
        String ret = "";
        int remain = length - binaryString.length();
        if (remain > 0) {
            for (int i = 0; i < remain; i++) {
                ret += "0";
            }
        }
        ret += binaryString;
        return ret;
    }

    private boolean combinationCompatible(Vector v) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        boolean compatible = true;
        Iterator it = v.iterator();
        while (it.hasNext()){
            String element = (String) it.next();
            Class t = Class.forName(element);
            Constructor cons = t.getDeclaredConstructor(Plan.class);
            Save save = (Save)cons.newInstance(new Plan());
            compatible = save.isCompatible(v);
        }
        return compatible;
    }




}
